package com.tuniu.agents.tools.train.tool;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.tuniu.agents.advisor.ChatEnvironmentMemory;
import com.tuniu.agents.message.ToolLoggerInfo;
import com.tuniu.agents.tool.EnvRequest;
import com.tuniu.agents.tools.train.TrainProperties;
import com.tuniu.agents.tools.train.constants.TrainCommonConstants;
import com.tuniu.agents.tools.train.enums.TrainSeatTypeEnum;
import com.tuniu.agents.tools.train.model.TrainCardData;
import com.tuniu.agents.tools.train.model.TrainDetailLinkData;
import com.tuniu.agents.tools.train.model.TrainMPDetailCardData;
import com.tuniu.agents.tools.train.utils.TrainCommonUtil;
import com.tuniu.agents.util.HttpManager;
import com.tuniu.agents.util.I18nUtil;
import com.tuniu.agents.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import static com.tuniu.agents.tools.train.TrainProperties.KEY_NAME;
import static com.tuniu.agents.tools.train.constants.TrainCommonConstants.SEAT_LIST_PRICE_DESC;
import static com.tuniu.agents.tools.train.enums.TrainSolutionEnum.getDescByType;
import static com.tuniu.agents.tools.train.enums.TrainSolutionEnum.getSolutionByType;
import static com.tuniu.agents.tools.train.utils.TrainCommonUtil.extractDate;
import static com.tuniu.agents.tools.train.utils.TrainCommonUtil.extractSubTime;

/**
 * Search train ticket list (Including: direct, same-train seat transfer, multi-segment purchase)
 */
public class SearchAllTrainTool implements BiFunction<SearchAllTrainTool.Request, ToolContext, SearchAllTrainTool.Response> {

    private static final Logger logger = LoggerFactory.getLogger(SearchAllTrainTool.class);

    public static final String TOOLNAME = "search_all_train";

    private final String searchAllTrainListUrl;

    private final String accessKey;

    @Autowired
    private HttpManager httpManager;

    @Autowired
    private ChatMemory chatMemory;

    @Autowired(required = false)
    private ChatEnvironmentMemory chatEnvironmentMemory;

    public SearchAllTrainTool(TrainProperties properties) {
        this.searchAllTrainListUrl = properties.getSearchAllTrainUrl();
        this.accessKey = properties.getAccessKey();
    }

    @Override
    @ToolLoggerInfo(beanName = "trainToolLoggingService")
    public Response apply(@EnvRequest Request request, ToolContext toolContext) {
        String conversationId = (String)toolContext.getContext().get("conversation_id");
        logger.info("SearchAllTrainTool apply conversationId:{}, request:{}",conversationId,JsonUtil.toJson(request));

        try {
            Response response = httpManager.post(searchAllTrainListUrl, request, Response.class, Map.of(KEY_NAME, accessKey));
            if(!response.successCode || CollectionUtils.isEmpty(response.data)){
                logger.error("SearchAllTrainTool apply fail or empty conversationId:{}, request:{},response:{}",conversationId, JsonUtil.toJson(request),JsonUtil.toJson(response));
                return null;
            }
            return response;
        }catch (Exception e){
            logger.error("SearchAllTrainTool apply error conversationId:{}, request:{}",conversationId, JsonUtil.toJson(request),e);
            return null;
        }
    }

    @JsonClassDescription("Train ticket query parameters")
    public record Request(

            @JsonProperty(value = "departureCityName",required = true)
            @JsonPropertyDescription("Departure city or station name, cannot be the same as arrivalCityName")
            String departureCityName,

            @JsonProperty(value = "arrivalCityName",required = true)
            @JsonPropertyDescription("Arrival city or station name, cannot be the same as departureCityName")
            String arrivalCityName,

            @JsonProperty(value = "departureDate",required = true)
            @JsonPropertyDescription("Departure date in yyyy-MM-dd format")
            String departureDate
    ) {}


    public record Response(
            @JsonProperty(value = "successCode") @JsonPropertyDescription("Success or failure flag")
            boolean successCode,

            @JsonProperty(value = "queryId") @JsonPropertyDescription("Snapshot Id")
            String queryId,

            @JsonProperty(value = "totalPageNum") @JsonPropertyDescription("Total number of pages")
            int totalPageNum,

            @JsonProperty(value = "data") @JsonPropertyDescription("Returned specific data")
            List<TrainTicket> data
    ) {
        public Response withData( List<TrainTicket> newData) {
            return new Response(
                    this.successCode(),
                    this.queryId(),
                    this.totalPageNum(),
                    newData
            );
        }
    }
    public record TrainTicket(
            @JsonProperty(value = "trainId") @JsonPropertyDescription("train Id")
            String trainId,
            @JsonProperty(value = "trainNum") @JsonPropertyDescription("train number")
            String trainNum,

            @JsonProperty(value = "departureCityName") @JsonPropertyDescription("Departure city")
            String departureCityName,

            @JsonProperty(value = "departureCityCode") @JsonPropertyDescription("Departure city code")
            String departureCityCode,

            @JsonProperty(value = "arrivalCityName") @JsonPropertyDescription("Arrival city")
            String arrivalCityName,

            @JsonProperty(value = "arrivalCityCode") @JsonPropertyDescription("Arrival city code")
            String arrivalCityCode,

            @JsonProperty(value = "departStationName") @JsonPropertyDescription("Departure station")
            String departStationName,

            @JsonProperty(value = "departStationCode") @JsonPropertyDescription("Departure station code")
            String departStationCode,

            @JsonProperty(value = "destStationName") @JsonPropertyDescription("Destination station")
            String destStationName,

            @JsonProperty(value = "destStationCode") @JsonPropertyDescription("Destination station code")
            String destStationCode,

            @JsonProperty(value = "trainType") @JsonPropertyDescription("Train type (direct/transfer/addition)")
            String trainType,

            @JsonProperty(value = "extraStation") @JsonPropertyDescription("Extra stations")
            String extraStation,

            @JsonProperty(value = "departureTime") @JsonPropertyDescription("Departure time yyyy-MM-dd HH:mm:ss")
            String departureTime,

            @JsonProperty(value = "arrivalTime") @JsonPropertyDescription("Arrival time")
            String arrivalTime,

            @JsonProperty(value = "duration") @JsonPropertyDescription("Journey duration")
            String duration,

            @JsonProperty(value = "durationDay") @JsonPropertyDescription("Number of days for journey")
            int durationDay,

            @JsonProperty(value = "supportTransferFlag") @JsonPropertyDescription("Support transfer flag")
            boolean supportTransferFlag,

            @JsonProperty(value = "supportSeatSelection") @JsonPropertyDescription("Support seat selection")
            boolean supportSeatSelection,

            @JsonProperty(value = "price") @JsonPropertyDescription("Ticket price information")
            TicketPrice price,

            @JsonProperty(value = "seatAvailable") @JsonPropertyDescription("Available seats information")
            SeatAvailable seatAvailable,

            @JsonProperty(value = "seatType") @JsonPropertyDescription("Seat type information")
            SeatType seatType,

            @JsonProperty(value = "url") @JsonPropertyDescription("Booking links")
            TrainOrderUrl url
    ) {}

    public record TrainOrderUrl(
            @JsonProperty(value = "appUrl") @JsonPropertyDescription("APP link")
            String appUrl,

            @JsonProperty(value = "mUrl") @JsonPropertyDescription("Mobile link")
            String mUrl,

            @JsonProperty(value = "mpUrl") @JsonPropertyDescription("Mini program link")
            String mpUrl
    ) {
        public TrainOrderUrl withUrl(String mUrl,String mpUrl) {
            return new TrainOrderUrl(this.appUrl, mUrl, mpUrl);
        }
    }

    public record TicketPrice(
            @JsonProperty(value = "gjrwPrice") @JsonPropertyDescription("Deluxe soft sleeper price")
            String gjrwPrice,

            @JsonProperty(value = "rwPrice") @JsonPropertyDescription("Soft sleeper price")
            String rwPrice,

            @JsonProperty(value = "rzPrice") @JsonPropertyDescription("Soft seat price")
            String rzPrice,

            @JsonProperty(value = "swzPrice") @JsonPropertyDescription("Business class seat price")
            String swzPrice,

            @JsonProperty(value = "tdzPrice") @JsonPropertyDescription("Special class seat price")
            String tdzPrice,

            @JsonProperty(value = "wzPrice") @JsonPropertyDescription("Standing ticket price")
            String wzPrice,

            @JsonProperty(value = "ywPrice") @JsonPropertyDescription("Hard sleeper price")
            String ywPrice,

            @JsonProperty(value = "yzPrice") @JsonPropertyDescription("Hard seat price")
            String yzPrice,

            @JsonProperty(value = "edzPrice") @JsonPropertyDescription("Second class seat price")
            String edzPrice,

            @JsonProperty(value = "ydzPrice") @JsonPropertyDescription("First class seat price")
            String ydzPrice,

            @JsonProperty(value = "dwPrice") @JsonPropertyDescription("High-speed sleeper price")
            String dwPrice,

            @JsonProperty(value = "ydwPrice") @JsonPropertyDescription("First class sleeper price")
            String ydwPrice,

            @JsonProperty(value = "edwPrice") @JsonPropertyDescription("Second class sleeper price")
            String edwPrice
    ) {}

    public record SeatAvailable(
            @JsonProperty(value = "gjrwNum") @JsonPropertyDescription("Available deluxe soft sleeper seats")
            Integer gjrwNum,

            @JsonProperty(value = "rwNum") @JsonPropertyDescription("Available soft sleeper seats")
            Integer rwNum,

            @JsonProperty(value = "rzNum") @JsonPropertyDescription("Available soft seats")
            Integer rzNum,

            @JsonProperty(value = "swzNum") @JsonPropertyDescription("Available business class seats")
            Integer swzNum,

            @JsonProperty(value = "tdzNum") @JsonPropertyDescription("Available special class seats")
            Integer tdzNum,

            @JsonProperty(value = "wzNum") @JsonPropertyDescription("Available standing tickets")
            Integer wzNum,

            @JsonProperty(value = "ywNum") @JsonPropertyDescription("Available hard sleeper seats")
            Integer ywNum,

            @JsonProperty(value = "yzNum") @JsonPropertyDescription("Available hard seats")
            Integer yzNum,

            @JsonProperty(value = "edzNum") @JsonPropertyDescription("Available second class seats")
            Integer edzNum,

            @JsonProperty(value = "ydzNum") @JsonPropertyDescription("Available first class seats")
            Integer ydzNum,

            @JsonProperty(value = "dwNum") @JsonPropertyDescription("Available high-speed sleeper seats")
            Integer dwNum,

            @JsonProperty(value = "ydwNum") @JsonPropertyDescription("Available first class sleeper seats")
            Integer ydwNum,

            @JsonProperty(value = "edwNum") @JsonPropertyDescription("Available second class sleeper seats")
            Integer edwNum
    ) {}

    public record SeatType(
            @JsonProperty(value = "gjrwType") @JsonPropertyDescription("Deluxe soft sleeper type")
            String gjrwType,

            @JsonProperty(value = "rwType") @JsonPropertyDescription("Soft sleeper type")
            String rwType,

            @JsonProperty(value = "rzType") @JsonPropertyDescription("Soft seat type")
            String rzType,

            @JsonProperty(value = "swzType") @JsonPropertyDescription("Business class seat type")
            String swzType,

            @JsonProperty(value = "tdzType") @JsonPropertyDescription("Special class seat type")
            String tdzType,

            @JsonProperty(value = "wzType") @JsonPropertyDescription("Standing ticket type")
            String wzType,

            @JsonProperty(value = "ywType") @JsonPropertyDescription("Hard sleeper type")
            String ywType,

            @JsonProperty(value = "yzType") @JsonPropertyDescription("Hard seat type")
            String yzType,

            @JsonProperty(value = "edzType") @JsonPropertyDescription("Second class seat type")
            String edzType,

            @JsonProperty(value = "ydzType") @JsonPropertyDescription("First class seat type")
            String ydzType,

            @JsonProperty(value = "dwType") @JsonPropertyDescription("High-speed sleeper type")
            String dwType,

            @JsonProperty(value = "ydwType") @JsonPropertyDescription("First class sleeper type")
            String ydwType,

            @JsonProperty(value = "edwType") @JsonPropertyDescription("Second class sleeper type")
            String edwType
    ) {}

    public static TrainCardData buildCardInfo(TrainTicket trainTicket,String conversationId) {
        if (trainTicket == null) {
            return null;
        }

        //获取会话id
        TrainCardData trainCardData = new TrainCardData();
        trainCardData.setDepartStationName(trainTicket.departStationName);
        trainCardData.setDestStationName(trainTicket.destStationName);
        trainCardData.setTrainNum(trainTicket.trainNum);
        trainCardData.setDepartureTime(trainTicket.departureTime);
        trainCardData.setArrivalTime(trainTicket.arrivalTime);
        trainCardData.setDuration(trainTicket.duration);

        // 构建Url
        buildListCardUrl(trainCardData,trainTicket);

        // 处理价格和座位信息
        if (trainTicket.price != null && trainTicket.seatAvailable != null) {
            TicketPrice ticketPrice = trainTicket.price;
            SeatAvailable seatInfo = trainTicket.seatAvailable;
            SeatType seatType = trainTicket.seatType;

            // 创建价格和余票的映射关系
            List<PriceSeatPair> priceSeatPairs = new ArrayList<>();
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.SWZ.getName(conversationId), ticketPrice.swzPrice, seatInfo.swzNum,seatType.swzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.YDZ.getName(conversationId), ticketPrice.ydzPrice, seatInfo.ydzNum, seatType.ydzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.EDZ.getName(conversationId), ticketPrice.edzPrice, seatInfo.edzNum, seatType.edzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.YZ.getName(conversationId), ticketPrice.yzPrice, seatInfo.yzNum, seatType.yzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.RW.getName(conversationId), ticketPrice.rwPrice, seatInfo.rwNum, seatType.rwType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.YW.getName(conversationId), ticketPrice.ywPrice, seatInfo.ywNum, seatType.ywType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.GJRW.getName(conversationId), ticketPrice.gjrwPrice, seatInfo.gjrwNum, seatType.gjrwType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.RZ.getName(conversationId), ticketPrice.rzPrice, seatInfo.rzNum, seatType.rzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.TDZ.getName(conversationId), ticketPrice.tdzPrice, seatInfo.tdzNum, seatType.tdzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.WZ.getName(conversationId), ticketPrice.wzPrice, seatInfo.wzNum, seatType.wzType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.DW.getName(conversationId), ticketPrice.dwPrice, seatInfo.dwNum, seatType.dwType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.YDW.getName(conversationId), ticketPrice.ydwPrice, seatInfo.ydwNum, seatType.ydwType,conversationId);
            addPriceSeatPair(priceSeatPairs, TrainSeatTypeEnum.EDW.getName(conversationId), ticketPrice.edwPrice, seatInfo.edwNum, seatType.edwType,conversationId);

            // Try to get the lowest price for seats with available tickets
            String minPrice = priceSeatPairs.stream()
                    .map(pair -> pair.price)
                    .filter(price -> price.compareTo(BigDecimal.ZERO) > 0) // Only consider prices greater than 0
                    .min(BigDecimal::compareTo)
                    .map(Objects::toString)
                    .orElse(null);

            // If no price found for available seats, get the lowest price from all seats
            if (minPrice == null) {
                minPrice = priceSeatPairs.stream()
                        .map(pair -> pair.price)
                        .filter(price -> price.compareTo(BigDecimal.ZERO) > 0) // Only consider prices greater than 0
                        .min(BigDecimal::compareTo)
                        .map(Objects::toString)
                        .orElse(null);
            }

            if(minPrice == null){
                return null;
            }

            trainCardData.setPrice(TrainCommonUtil.formatPrice(minPrice));

            // Set seat information
            List<TrainCardData.SeatInfo> trainTypes = priceSeatPairs.stream()
                    .map(pair -> pair.seatInfo)
                    .collect(Collectors.toList());
            trainCardData.setTrainType(trainTypes);
        }

        // Add tags (whether second class seats support same-train seat transfer, etc.)
        if (trainTicket.seatType != null && trainTicket.seatType.edzType != null) {
            trainCardData.setEdzSeatType(trainTicket.seatType.edzType);
        }

        return trainCardData;
    }


    // Data class for price and available seats information
    private static class PriceSeatPair {
        BigDecimal price;
        int leftNumber;
        String seatName;
        TrainCardData.SeatInfo seatInfo;


        PriceSeatPair(String seatName, BigDecimal price, int leftNumber, String seatType,String conversationId) {
            this.seatName = seatName;
            this.price = price;
            this.leftNumber = leftNumber;

            // Create seat information
            this.seatInfo = new TrainCardData.SeatInfo();
            this.seatInfo.setSeatType(seatType);
            this.seatInfo.setSeatName(seatName);
            this.seatInfo.setLeftNumber(leftNumber);

            String typeDesc = getDescByType(seatType,conversationId);

            String leftNumberDesc = null;
            if(leftNumber == 0){
                leftNumberDesc = I18nUtil.getMessage(conversationId,TrainCommonConstants.SEAT_EMPTY_TAG);
            }else{
                leftNumberDesc = leftNumber > 50 ? typeDesc + I18nUtil.getMessage(conversationId,TrainCommonConstants.SEAT_ENOUGH_TAG) : typeDesc + leftNumber + " " + I18nUtil.getMessage(conversationId,TrainCommonConstants.SEAT_DESC);
            }
            this.seatInfo.setLeftNumberDesc(leftNumberDesc);
        }
    }

    // Helper method to add price and available seats information
    private static void addPriceSeatPair(List<PriceSeatPair> pairs,
                                         String seatName,
                                         String priceStr,
                                         Integer leftNum,
                                         String seatType,
                                         String conversationId) {
        if (StringUtils.hasText(priceStr) && leftNum != null && leftNum >= 0) {
            pairs.add(new PriceSeatPair(
                    seatName,
                    new BigDecimal(priceStr),
                    leftNum,
                    seatType,
                    conversationId
            ));
        }
    }


    private static void buildListCardUrl(TrainCardData trainCardData, TrainTicket trainTicket) {

        // App detail jump link
        String appLink = null;
        try {
            appLink = TrainCommonConstants.APP_TRAIN_DETAIL_URL + URLEncoder.encode(JsonUtil.toJson(convertListDataToTrainDetailLinkData(trainTicket)),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        // Mini program jump link
        TrainMPDetailCardData trainMPDetailCardData = new TrainMPDetailCardData();
        trainMPDetailCardData.setTrainId(trainTicket.trainId);
        trainMPDetailCardData.setDepartureCityName(trainTicket.departureCityName);
        trainMPDetailCardData.setDepartureCityCode(trainTicket.departureCityCode);
        trainMPDetailCardData.setArrivalCityName(trainTicket.arrivalCityName);
        trainMPDetailCardData.setArrivalCityCode(trainTicket.arrivalCityCode);
        trainMPDetailCardData.setDepartureDate(extractDate(trainTicket.departureTime));
        trainMPDetailCardData.setTrainNum(trainTicket.trainNum);
        trainMPDetailCardData.setDepartDepartTime(extractSubTime(trainTicket.departureTime));
        trainMPDetailCardData.setDestArriveTime(extractSubTime(trainTicket.arrivalTime));
        StringBuilder mpUrl = null;
        try {
            mpUrl = new StringBuilder()
                    .append(TrainCommonConstants.MP_TRAIN_DETAIL_URL)
                    .append("?filterGD=0&params=").append(URLEncoder.encode(JsonUtil.toJson(trainMPDetailCardData), "UTF-8"));

        } catch (UnsupportedEncodingException e) {
            logger.warn("SearchAllTrainTool buildListCardUrl build mpUrl error trainTicket:{}",JsonUtil.toJson(trainTicket),e);
        }

        // Build UrlInfo
        TrainCardData.UrlInfo urlInfo = new TrainCardData.UrlInfo();
        urlInfo.setAppUrl(appLink);
        urlInfo.setMpUrl(mpUrl.toString());
        trainCardData.setUrl(urlInfo);

    }

    /**
     * @param trainCardData
     * @param saveInMdReason Recommendation reason for this train number
     * @return
     */
    public static void buildMarkdownAssistantMsg(StringBuilder markdownTrainRecommendations,TrainCardData trainCardData, String saveInMdReason,String conversationId) {
        if(markdownTrainRecommendations.isEmpty()){
            markdownTrainRecommendations.append("| Train Number | Departure Station | Arrival Station | Departure Time | Arrival Time | Recommendation Reason |\n");
            markdownTrainRecommendations.append("|--------|--------|--------|----------|----------|----------|\n");
        }
        String stationDesc = I18nUtil.getMessage(conversationId, "train.template.station.desc");
        markdownTrainRecommendations.append("|").append(trainCardData.getTrainNum())
                .append("|").append(trainCardData.getDepartStationName()+stationDesc)
                .append("|").append(trainCardData.getDestStationName()+stationDesc)
                .append("|").append(trainCardData.getDepartureTime())
                .append("|").append(trainCardData.getArrivalTime())
                .append("|").append(saveInMdReason)
                .append("|\n");
    }

    /**
     * Convert train ticket list to markdown format
     */
    public static String convertJsonToMark(String result,String conversationId) {
        StringBuilder resultBuilder = new StringBuilder();

        // Table header
        resultBuilder.append("| 车次号 | 出发站 | 到达站 | 出发时间 | 到达时间 | 运行耗时 | 座位信息 |\n");
        resultBuilder.append("|--------|--------|--------|----------|----------|----------|----------|\n");

        // Deserialize data
        SearchAllTrainTool.Response response = JsonUtil.fromJson(result, SearchAllTrainTool.Response.class);

        if (response == null || response.data() == null) {
            return resultBuilder.toString();
        }
        String stationDesc = I18nUtil.getMessage(conversationId, "train.template.station.desc");

        for (SearchAllTrainTool.TrainTicket train : response.data()) {
            StringBuilder rowBuilder = new StringBuilder();

            // Get seat information
            String seatInfo = addAvailableSeats(train,conversationId);

            //id related
            //rowBuilder.append("|").append(generateNumericTrainId(train.trainNum(),train.departureTime(),train.arrivalTime()))

            // Add basic information
            rowBuilder
                    .append("|").append(train.trainNum())
                    .append("|").append(train.departStationName()+stationDesc)
                    .append("|").append(train.destStationName()+stationDesc)
                    .append("|").append(train.departureTime())
                    .append("|").append(train.arrivalTime())
                    .append("|").append(train.duration())
                    .append("|").append(seatInfo)
                    .append("|\n");


            resultBuilder.append(rowBuilder);
        }

        return resultBuilder.toString();
    }

    /**
     * Add seat information
     */
    private static String addAvailableSeats(SearchAllTrainTool.TrainTicket trainTicket,String conversationId) {
        StringBuilder seatInfoBuilder = new StringBuilder();
        // Convert three objects to JsonMap
        Map<String, String> seatAvailableMap = JsonUtil.toMap(JsonUtil.toJson(trainTicket.seatAvailable()),String.class);
        Map<String, String> priceMap = JsonUtil.toMap(JsonUtil.toJson(trainTicket.price()),String.class);
        Map<String, String> seatTypeMap = JsonUtil.toMap(JsonUtil.toJson(trainTicket.seatType()),String.class);

        // Iterate through enum to handle all seat types
        for (TrainSeatTypeEnum seatEnum : TrainSeatTypeEnum.values()) {
            String name = seatEnum.getName(conversationId);
            String seatNum = seatAvailableMap.get(seatEnum.getNumField());
            String seatPrice = priceMap.get(seatEnum.getPriceField());
            String seatType = seatTypeMap.get(seatEnum.getTypeField());

            //When quantity and price are not null
            if(seatNum != null && seatPrice != null){
                seatInfoBuilder.append(I18nUtil.getMessage(conversationId,SEAT_LIST_PRICE_DESC,new Object[]{ name, seatPrice, seatNum, getSolutionByType(seatType,conversationId)}));
            }
        }
        return seatInfoBuilder.toString();

    }

    /**
     * Convert list interface data to app detail jump data
     * @return
     */
    public static TrainDetailLinkData convertListDataToTrainDetailLinkData(TrainTicket trainTicket) {
        TrainDetailLinkData data = new TrainDetailLinkData();
        data.setDepartDate(extractDate(trainTicket.departureTime()));
        data.setDepartCityCode(trainTicket.departureCityCode());
        data.setDepartCityName(trainTicket.departureCityName());
        data.setDestCityCode(trainTicket.arrivalCityCode());
        data.setDestCityName(trainTicket.arrivalCityName());

        //set detail data
        TrainDetailLinkData.Detail detail = new TrainDetailLinkData.Detail();
        detail.setTrainId(trainTicket.trainId());
        detail.setTrainNum(trainTicket.trainNum());
        detail.setDepartStationName(trainTicket.departStationName());
        detail.setDepartStationCode(trainTicket.departStationCode());
        detail.setDestStationName(trainTicket.destStationName());
        detail.setDestStationCode(trainTicket.destStationCode());
        detail.setDepartDepartTime(extractSubTime(trainTicket.departureTime()));
        detail.setDestArriveTime(extractSubTime(trainTicket.arrivalTime()));
        detail.setDuration(trainTicket.duration());
        detail.setDurationDay(trainTicket.durationDay());
        detail.setSupportSeatSelection(trainTicket.supportSeatSelection());
        detail.setSupportTransferFlag(trainTicket.supportTransferFlag());
        data.setDetail(detail);

        return data;
    }

}
